<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
  html { height: 100%; }
  body { height: 100%; margin: 0px; padding: 0px; }
  #map_canvas { height: 100%; }
</style>
<script src="js/sprintf-0.7-beta1.js"></script>
<script src="js/translations.js"></script>
<script type="text/javascript">
DEFAULT_ZOOM=14;

/* I18N functions */
function _(s) {
	if (typeof(i18n)!='undefined' && i18n[s])
		return i18n[s];
	return s;
}

function add_menu_item(name, value) {
    if(window.droid) 
    	droid.addOptionsMenuItem(name, 'menu', value);
}

function on_menu(ev) {
    var cmd = ev.data;
    if(cmd == 'dest') {
        dest = prompt(_("destination"), window.requested_destination || '');
        if(dest) {
            window.requested_destination = dest;
            if(window.position)
                route(window.position, dest, false, predict_waypoint(navi));
        }
    } else if(window.destination && cmd == 'recalc') {
         var _wp = (predict_waypoint(navi) || []).concat(window.wp && window.wp.slice(navi.leg) || [])
         route(window.position, window.destination, true, _wp)
    }
}  

function predict_waypoint(navi) {
    var pp = navi.predict_position(10)
    return pp && [{location:pp, stopover:false}]
}

function parse_opts() {
   var qs = document.location.href.split(/[#\?]/).slice(1).join('&')
   qs = qs.split('&').map(function(s) {return s.split('=').map(decodeURIComponent);})
   var opts = {}
   for(var i=0; i<qs.length; i++) 
       opts[qs[i][0]] = qs[i][1];
   return opts
}

function boot() {
   opts = parse_opts();
   window.requested_destination = opts.dest || '';
   var lang = opts.language || 'en';
   window.i18n = window['i18n_' + lang]
   var script = document.createElement("script");
   script.type = "text/javascript";
   script.src = "http://maps.google.com/maps/api/js?sensor=true&callback=initialize&language=" + lang;
   document.body.appendChild(script);
}

function strip_tags(s) {
    return s.replace(/(<([^>]+)>)/ig, " ").replace(/ +/g,' ').replace(/ w \d+ w /,' w ');
}

function computeDistanceBetween(p1, p2) {
    var R = 6371000;
    var lat1 = p1.lat()
    var lon1 = p1.lng()
    var lat2 = p2.lat()
    var lon2 = p2.lng()
    var dLat = (lat2-lat1) * Math.PI / 180;
    var dLon = (lon2-lon1) * Math.PI / 180; 
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * 
            Math.sin(dLon/2) * Math.sin(dLon/2); 
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    var d = R * c;
    return d;
}

function distance_from(p, p1, p2) {
  var x1 = p1.lng()
  var y1 = p1.lat()

  var x2 = p2.lng()
  var y2 = p2.lat()
  
  var x3 = p.lng()
  var y3 = p.lat()

  var dx = (x2-x1)
  var dy = (y2-y1)

  var k = Math.cos(Math.PI / 180 * y3)

  var A1 = dy
  var B1 = - dx
  var C1 = - A1*x1 - B1*y1
    
  var A2 = dx
  var B2 = dy / k / k
  var C2 = - A2*x3 - B2 * y3
    
  var WG = A1*B2 - A2*B1
  var x = (- C1*B2 + C2*B1) / WG
  var y = (- A1*C2 + A2*C1) / WG
  var mid = new google.maps.LatLng(y, x)
  var between = x>=Math.min(x1, x2) && x<=Math.max(x1, x2) &&
  y>=Math.min(y1,y2) && y<=Math.max(y1,y2)
  if(between) 
    return computeDistanceBetween(p, mid);
  else
    return Math.min(computeDistanceBetween(p, p1), computeDistanceBetween(p, p2))
}

function say(txt) {
    if(window.droid) {
        droid.makeToast(txt);
        droid.ttsSpeak(txt);
    } else {
        console.log(txt);
    }
}

function where_i_am(p, initial) {
    geocoder.geocode({location:p}, function(data) {
        if(data.length) {
            say(data[0].formatted_address);
            if(initial)
              _ga(p.toString());
        }
    })
}    

function Navi() {
    DISTS =  [50, 100, 200, 300, 500];
    var route = null;
    var leg = null;
    var step = null;
    var substep = null;
    var dist = null;

    _this = this;
    _this.distance = null;

    function set_step(new_leg, new_step, new_substep) {
        var step_change = (leg != new_leg || step != new_step);
        leg = _this.leg = new_leg;
        step = _this.step = new_step;
    	substep = _this.substep = new_substep;
    	if(step_change) {
    	    dist=null;
    	    _this.on_step_change(route.legs[leg].steps[step]);
    	}
    	_this.on_substep_change(route.legs[leg].steps[new_step], new_substep);
    }
    
    _this.track_distance = function(pos) {
        _this.distance = d = _this.distance_to_step_end(pos);
        var new_dist = dist && Math.min(dist, d) || d;
        for(var i=0; i<DISTS.length; i++) {
            if(!dist || dist && new_dist < DISTS[i] && dist>DISTS[i]) {
                var dd = Math.floor(new_dist/10)*10
                var inst = step+1 < route.legs[leg].steps.length ? strip_tags(route.legs[leg].steps[step+1].instructions) : _("End of route")
                var txt = (dd>25 ? sprintf(_("in %s"), pretty_dist(dd)) + " ": "") + inst;
                say(txt);
                break;
            }
        }
        dist = new_dist;
    }
    function cache_distances(path) {
        path.lengths = []
        path.distances = []
        var dist_to_end = 0;
        if(path.length<2) console.error("WTF, path length < 2")
        for(var i=path.length-2; i>=0; i--) {
            var d = computeDistanceBetween(path[i], path[i+1]);
            dist_to_end += d;
            path.lengths.push(d);
            path.distances.push(dist_to_end);
        }
        path.lengths.reverse()
        path.distances.reverse()
        path.distances.push(0);
    }
    
    function path_length(path, i) {
        if(!path.lengths)
            cache_distances(path);
        return path.lengths[i];
    }
    
    function distance_to_end(path, i) {
        if(!path.distances) 
            cache_distances(path)
        return path.distances[i];
    }

    _this.move = function(pos) {
        var path_dist = 0;
        var min_dist = null;
        var min_leg = null;
        var min_step = null;
        var min_substep = null
        var cur_leg = min_leg = leg;
        var cur_step = min_step = step;
        var i = min_substep = substep;

        while(path_dist < 1000 && cur_leg < route.legs.length) {
            var path = route.legs[cur_leg].steps[cur_step].path;
            var d = distance_from(pos, path[i], path[i+1])
            if(min_dist)
                path_dist += path_length(path, i);//computeDistanceBetween(path[i], path[i+1]);
            if(min_dist == null || d < min_dist) {
                min_dist = d;
                min_leg = cur_leg;
                min_step = cur_step;
                min_substep = i;
            }
            i += 1
            if(i >= path.length-1) {
                i = 0;
                cur_step += 1;
                if(cur_step>=route.legs[cur_leg].steps.length) {
                    cur_step = 0;
                    cur_leg += 1;
                }
            }
        }
        if(min_leg!=leg || min_step != step || min_substep != substep)
            set_step(min_leg, min_step, min_substep);
    }
    
    _this.distance_to_step_end = function(pos) {
        var path = route.legs[leg].steps[step].path;
        return computeDistanceBetween(pos, path[substep+1]) + distance_to_end(path, substep+1)
    }
    
    _this.update_position = function(pos) {
        _this.position = pos
	if(_this.ve) {
	    var len = computeDistanceBetween(pos, _this.ve);
            if(len > 25) {
                _this.vb = _this.ve;
                _this.ve = pos;
                _this.vlen = len;
            }
	} else
	    _this.ve = pos;
        if(route) {
            _this.move(pos);
            _this.track_distance(pos);
        }
    }
    
    _this.predict_position = function(distance) {
        if(!_this.vlen || _this.vlen > 5000) return null;
        var k = distance / _this.vlen;
        var lat = _this.position.lat() + (_this.ve.lat() - _this.vb.lat()) * k
        var lng = _this.position.lng() + (_this.ve.lng() - _this.vb.lng()) * k
        return new google.maps.LatLng(lat, lng)
    }
      
    _this.set_route = function(response) {
        route = _this.route = response.routes[0]
        set_step(0, 0, 0);
        if(_this.position)
            _this.update_position(_this.position);
        say(strip_tags(route.legs[leg].steps[step].instructions))
    }
    
    _this.on_step_change = function(s) {
    }
    
    _this.on_substep_change = function(s, i) {
    }
}

function center(p) {
    autopan = true;
    map.panTo(p);
}

function resolve_waypoints(wp, cb) {
    var extra = opts.local && ' ' + opts.local || '';
    var i=0;
    var result=[];
    function geo_cb(data) {
        result.push({location:data[0].geometry.location, stopover:true});
        if(wp.length) {
            geocoder.geocode({address:wp.shift() + extra}, geo_cb);
        } else 
            cb(result);
    }
    geocoder.geocode({address:wp.shift() + extra}, geo_cb);
}

function pretty_dist(dist) {
  if(dist<10000) {
    var round = dist<=1000 ? 10 : 100;
    return sprintf(_("%s meters"), Math.round(dist/round)*round);
  } else
    return sprintf(_("%s kilometers"), Math.round(dist/1000));
}

function route(src, dest_spec, silent, waypoints) {
    if(!src) {
        say(_('Your position is unknown'));
    	return;
    }
    if(!dest_spec) {
        say(_('Destination not set'));
        return;
    }
    if(dest_spec.split) {
        var wp = dest_spec.split(' ' + _("via") + ' ');
        var extra = opts.local && ' ' + opts.local || '';
        dest = wp.shift() + extra;
        if(wp.length) {
            console.log('resolve waypoints', wp);
            resolve_waypoints(wp, function(waypoints) {
                route(src, dest, silent, waypoints);
            });
            return;
        }
    } else {
        dest = dest_spec;
    }
    var m;
    if(m=dest.match(/pieszo|piechotą|na piechotę/)) {
        dest = dest.replace(m[0],'')
        mode = google.maps.DirectionsTravelMode.WALKING
    } else if(m=dest.match(/rower(em)?/)) {
        dest = dest.replace(m[0],'')
        mode = google.maps.DirectionsTravelMode.BICYCLING
    } else {
        mode = google.maps.DirectionsTravelMode.DRIVING
    }
    var request = {
        origin:src,
        destination:dest,
        travelMode: mode,
        waypoints:waypoints
    };
    
    directionsService.route(request, function(response, status) {
        if(status == google.maps.DirectionsStatus.OK) {
            var legs = response.routes[0].legs;
            var total = 0;
            for(var i=0; i<legs.length; i++) total += legs[i].distance.value
            say(sprintf(_("Route calculated, length %s"), pretty_dist(total)));
            if(silent || !window.destination || confirm(_('Setup route?'))) {
                directionsDisplay.setDirections(response);
                navi.set_route(response);
                center(legs[0].steps[0].start_point);
                window.destination = dest_spec;
                window.wp = waypoints && waypoints.filter(function(w){return w.stopover}) || [];
            }
        } else
            say(sprintf(_("Route to %s not found"), request.destination))
    });
}

function is_position_set() {
  if(window.position) return true;
    say("nie określono pozycji");
}
  
function initialize() {
    
    directionsService = new google.maps.DirectionsService();
    geocoder = new google.maps.Geocoder()
    directionsDisplay = new google.maps.DirectionsRenderer({preserveViewport:true});
    
    autopan = true;
    
    var myOptions = {
      zoom: 1,
      center: new google.maps.LatLng(0, 0),
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    window.map = new google.maps.Map(document.getElementById("map_canvas"),
        myOptions);
    directionsDisplay.setMap(map);
    var me_marker = new google.maps.Marker({map:map})
    var next_step_marker = new google.maps.Marker({map:map})
    current_fragment = new google.maps.Polyline({map:map,strokeColor:"red"})

    google.maps.event.addListener(map, 'click', function() {
            droid.eventPost('recognize','')
    });
    google.maps.event.addListener(map, 'dragstart', function() {
            autopan = false;
    });

    navi = new Navi();
    
    navi.on_step_change = function(step) {
        next_step_marker.setPosition(step.end_point);
//        var path = steps[n].path;
//        for(var i=0; i<path.length; i++)
//            new google.maps.Marker({map:map, position:path[i]})
    }
    
    navi.on_substep_change = function(step, i) {
        if(window.current_fragment) {
            window.current_fragment.setPath(step.path.slice(i, i+5))
        }
    }
    
    function locating_speed(fast) {
        if(window.speed != fast) {
            if(window.droid) {
                if(fast)
                    droid.startLocating(1000, 25);
                else
                    droid.startLocating(60000, 100);
            }
            window.speed = fast;
        }
    }
    
    function update_position(p, speed, update) {
        if(!window.position) {
          where_i_am(p, true);
          if(window.requested_destination)
              route(p, window.requested_destination)
          map.setCenter(p);
          map.setZoom(DEFAULT_ZOOM);
        }
        window.position = p;
        if(update) {
        	me_marker.setPosition(p)
        	// implement more intelligent panning, add some margin
        	var bounds = map.getBounds()
        	if(autopan && bounds && !bounds.contains(p))
        	    map.panTo(p);
        }
        navi.update_position(p);
        if(navi.distance && window.droid) {
            locating_speed(navi.distance<1000);
        }
//        new google.maps.Marker({map:map, position:p})
    }
    
  
  var last_time = 0;
  function on_location(ev) {
    var update = droid.checkScreenOn()
    if(ev.data['gps']) {
      var g = ev.data.gps;
      // hack to eliminate doubles
      if(g.time>last_time) {
        update_position(new google.maps.LatLng(g.latitude, g.longitude), g.speed, update);
        last_time = g.time;
      }
    }
  }
  

  function on_recognized(ev) {
    var cmd = ev.data;
    window.droid && droid.makeToast(cmd);
    if(cmd == _('map')) 
      map.setMapTypeId(google.maps.MapTypeId.ROADMAP)
    else if(cmd == _('satellite'))
      map.setMapTypeId(google.maps.MapTypeId.SATELLITE)
    else if(cmd == _('center') && is_position_set())
        center(window.position);
    else if(cmd == _('where i am') && is_position_set())
        where_i_am(window.position);
    else {
        window.requested_destination = cmd
        if(window.position)
            route(window.position, cmd);
    }
  }

  window.droid = window.Android && new Android();

  add_menu_item(_('Dest'), 'dest')  
  add_menu_item(_('Recalc'), 'recalc')

  if(window.droid) {
      droid.registerCallback("location", on_location);
      droid.registerCallback("recognize_result", on_recognized);
      droid.registerCallback("menu", on_menu);
      droid.startLocating(1000, 25);
  } else {
     document.getElementById("top").style.cssText="display:block";
     me_marker.setDraggable(true);
//     update_position(map.getCenter(), 0, true);
     navigator.geolocation.getCurrentPosition(function(pos) {
       var pos = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);
       update_position(pos, 0, true);
     });
    google.maps.event.addListener(me_marker, 'position_changed', function() {
       update_position(this.getPosition(), 0);
    });
  }

  }
  
function calc_route() {
    if(is_position_set())
        route(window.position, document.getElementById('dest').value, true, predict_waypoint(navi));
}

function _ga(pos) {
  _gaq = [];
  _gaq.push(['_setAccount', 'UA-8277061-1']);
  _gaq.push(['_setCustomVar', 1, 'Pos', pos]);
  _gaq.push(['_trackPageview']);
  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(ga, s);
  })();
}
</script>
</head>
<body onload="boot()">
  <div style="display:none" id="top">
   <input type="text" x-webkit-speech id="dest" />
   <input type="button" onclick="calc_route();" value="wyznacz"/>
  </div>
  <div id="map_canvas" style="width:100%;height:100%;"></div>
</body>
</html>
